package com.me.pikachu.main;

import java.util.Stack;

public class Point {

	// the point that mean everything is done.
	// put on last element in stack
	private Point LAST_POINT;

	public static final int COMPARE = -10;

	public int i;

	public int j;

	private int[][] matrix;

	private float screenI;

	public Point(int i, int j, int[][] matrix) {
		this.i = i;
		this.j = j;
		this.matrix = matrix;
		LAST_POINT = new Point(-100, -100);
	}

	private float screenJ;

	public Point(int i, int j) {
		this.i = i;
		this.j = j;
	}

	public Point(float i, float j) {
		this.screenI = i;
		this.screenJ = j;
	}

	public float getScreenI() {
		return screenI;
	}

	public float getScreenJ() {
		return screenJ;
	}

	public boolean isComparePoint() {
		if (matrix[i][j] == COMPARE)
			return true;
		else
			return false;
	}

	public Stack<Point> getPath(Point dest) {
		Stack<Point> path = new Stack<Point>();
		// if (matrix[i][j] == matrix[dest.i][dest.j]) {
		path = getLine(this, dest);
		if (!path.pop().equals(LAST_POINT)) {
			Stack<Point> right, left, up, down;
			right = findRight(dest);
			left = findLeft(dest);
			up = findUp(dest);
			down = findDown(dest);

			// get min path
			int min = 10000;
			if (right.pop().equals(LAST_POINT) && min > right.size())
				min = right.size();
			if (left.pop().equals(LAST_POINT) && min > left.size())
				min = left.size();
			if (up.pop().equals(LAST_POINT) && min > up.size())
				min = up.size();
			if (down.pop().equals(LAST_POINT) && min > down.size())
				min = down.size();

			// set path we want
			if (left.size() == min)
				path = left;
			if (right.size() == min)
				path = right;
			if (down.size() == min)
				path = down;
			if (up.size() == min)
				path = up;
		}
		return path;
	}

	private boolean isEqual(Point src, Point dest) {
		return (src.i == dest.i && src.j == dest.j) ? true : false;
	}

	private Stack<Point> findRight(Point dest) {
		Stack<Point> path = new Stack<Point>();
		path.push(this);

		int j0 = j + 1;
		int i0 = i;
		// horizon
		while (matrix[i0][j0] == COMPARE) {
			Point p = new Point(i0, j0);
			path.push(p);

			Point temp = new Point(dest.i, j0);
			Stack<Point> column = getLine(p, temp);
			if (column.pop().equals(LAST_POINT)) {
				if (isEqual(temp, dest)) {
					for (int i = 1; i < column.size(); i++) {
						path.push(column.get(i));
					}
					path.push(LAST_POINT);
					break;
				} else {
					Stack<Point> row = getLine(temp, dest);
					if (row.pop().equals(LAST_POINT)) {
						for (int i = 1; i < column.size(); i++) {
							path.push(column.get(i));
						}
						for (int i = 1; i < row.size(); i++) {
							path.push(row.get(i));
						}
						path.push(dest);
						path.push(LAST_POINT);
						break;
					}
				}
			}

			j0++;
			if (j0 == matrix[0].length)
				break;
		}

		return path;
	}

	private Stack<Point> findLeft(Point dest) {
		Stack<Point> path = new Stack<Point>();
		path.push(this);

		int j0 = j - 1;
		int i0 = i;
		// horizon
		while (matrix[i0][j0] == COMPARE) {
			Point p = new Point(i0, j0);
			path.push(p);

			Point temp = new Point(dest.i, j0);
			Stack<Point> column = getLine(p, temp);
			if (column.pop().equals(LAST_POINT)) {
				if (isEqual(temp, dest)) {
					for (int i = 1; i < column.size(); i++) {
						path.push(column.get(i));
					}
					path.push(LAST_POINT);
					break;
				} else {
					Stack<Point> row = getLine(temp, dest);
					if (row.pop().equals(LAST_POINT)) {
						for (int i = 1; i < column.size(); i++) {
							path.push(column.get(i));
						}
						for (int i = 1; i < row.size(); i++) {
							path.push(row.get(i));
						}
						path.push(LAST_POINT);
						break;
					}
				}
			}
			j0--;
			if (j0 < 0)
				break;
		}

		return path;
	}

	private Stack<Point> findUp(Point dest) {
		Stack<Point> path = new Stack<Point>();
		path.push(this);

		int j0 = j;
		int i0 = i - 1;
		// vertical
		while (matrix[i0][j0] == COMPARE) {
			Point p = new Point(i0, j0);
			path.push(p);

			Point temp = new Point(i0, dest.j);
			Stack<Point> row = getLine(p, temp);
			if (row.pop().equals(LAST_POINT)) {
				if (isEqual(temp, dest)) {
					for (int i = 1; i < row.size(); i++) {
						path.push(row.get(i));
					}
					path.push(LAST_POINT);
					break;
				} else {
					Stack<Point> column = getLine(temp, dest);
					if (column.pop().equals(LAST_POINT)) {
						for (int i = 1; i < row.size(); i++) {
							path.push(row.get(i));
						}
						for (int i = 1; i < column.size(); i++) {
							path.push(column.get(i));
						}
						path.push(LAST_POINT);
						break;
					}
				}
			}
			i0--;
			if (i0 < 0)
				break;
		}

		return path;
	}

	private Stack<Point> findDown(Point dest) {

		Stack<Point> path = new Stack<Point>();
		path.push(this);

		int j0 = j;
		int i0 = i + 1;
		// vertical
		while (matrix[i0][j0] == COMPARE) {
			Point p = new Point(i0, j0);
			path.push(p);

			Point temp = new Point(i0, dest.j);
			Stack<Point> row = getLine(p, temp);
			if (row.pop().equals(LAST_POINT)) {
				if (isEqual(temp, dest)) {
					for (int i = 1; i < row.size(); i++) {
						path.push(row.get(i));
					}
					path.push(LAST_POINT);
					break;
				} else {
					Stack<Point> column = getLine(temp, dest);
					if (column.pop().equals(LAST_POINT)) {
						for (int i = 1; i < row.size(); i++) {
							path.push(row.get(i));
						}
						for (int i = 1; i < column.size(); i++) {
							path.push(column.get(i));
						}
						path.push(LAST_POINT);
						break;
					}
				}
			}
			i0++;
			if (i0 == matrix.length)
				break;
		}

		return path;
	}

	/**
	 * define line: 1: in Matrix has COMPARE between source and destination
	 * example: [2 0 0 0 0 2], COMPARE=0 or [0 0 0 0] 2: source and destination
	 * are neighbor 3: source has 0
	 * 
	 * return Stack that contain path, but destination and source are neigh just
	 * two point
	 */
	private Stack<Point> getLine(Point src, Point dest) {

		Stack<Point> line = new Stack<Point>();
		line.push(src);

		if (src.i == dest.i) {
			// if source and destination are neighbor
			if (Math.abs(src.j - dest.j) == 1) {
				if ((matrix[src.i][src.j] == matrix[dest.i][dest.j])
						|| (matrix[src.i][src.j] == COMPARE))
					line.push(dest);
			} else {
				if (src.j > dest.j) {
					int i = src.i;
					for (int j = src.j - 1; j > dest.j; j--) {
						if (matrix[i][j] == COMPARE) {
							Point p = new Point(i, j);
							line.push(p);
						}
					}

				} else {
					int i = src.i;
					for (int j = src.j + 1; j < dest.j; j++) {
						if (matrix[i][j] == COMPARE) {
							Point p = new Point(i, j);
							line.push(p);
						}
					}
				}
				line.push(dest);
			}// end Math.abs(src.j - dest.j) == 1
			if (line.size() - 1 == Math.abs(src.j - dest.j))
				line.push(LAST_POINT);
		} else if (src.j == dest.j) {
			// if source and destination are neighbor
			if (Math.abs(src.i - dest.i) == 1) {
				if ((matrix[src.i][src.j] == matrix[dest.i][dest.j])
						|| (matrix[src.i][src.j] == COMPARE))
					line.push(dest);
			} else {
				if (src.i > dest.i) {
					int j = src.j;
					for (int i = src.i - 1; i > dest.i; i--) {
						if (matrix[i][j] == COMPARE) {
							Point p = new Point(i, j);
							line.push(p);
						}
					}
				} else {
					int j = src.j;
					for (int i = src.i + 1; i < dest.i; i++) {
						if (matrix[i][j] == COMPARE) {
							Point p = new Point(i, j);
							line.push(p);
						}
					}
				}
				line.push(dest);
			}// end Math.abs(src.i - dest.i) == 1
			if (line.size() - 1 == Math.abs(src.i - dest.i))
				line.push(LAST_POINT);
		}// end src.j == dest.j

		return line;
	}
}
